/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package coalescent.nb;

import coalescent.cef.phylo.GeneTreeDrawer_Base;
import coalescent.cef.pui.pob.Builders_Coa;
import coalescent.data.K69Data;
import coalescent.sampledata.K69DataDNDataFileBased;
import commons.nb.data.DNData;
import commons.nb.data.DNDataChangeEvent;
import commons.nb.data.DNDataChangeListener;
import commons.nb.data.gui.DataNode;
import commons.util.GUIUtil;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import org.netbeans.spi.navigator.NavigatorPanel;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static coalescent.phylogeny.GeneTree.Edge;
import static coalescent.phylogeny.GeneTree.Node;

/**
 * @author Susanta Tewari
 * @version version
 * @history Created on 10/16/13.
 * @since since
 */
public class GeneTreeDrawer_Navigator implements NavigatorPanel, LookupListener {

    private final Map<String, GeneTreeDrawer_Base> viewer_map = new HashMap<>();
    private Lookup.Result<DataNode> result;
    private DNData dnData;
    static GeneTreeDrawer_Navigator instance;
    private ModalGraphMouse.Mode mode    = ModalGraphMouse.Mode.TRANSFORMING;
    private final JLabel nullDataDisplay = new JLabel();    // displayed when dnData is null

    public GeneTreeDrawer_Navigator() {
        instance = this;
    }

    @Override
    public String getDisplayName() {
        return "displayName";
    }

    @Override
    public String getDisplayHint() {
        return "displayHint";
    }

    /**
     * saves the layout of applicable drawers
     */
    static void store() {

        for (GeneTreeDrawer_Base drawer : instance.viewer_map.values()) {
            if (drawer.isLayoutSavable()) drawer.saveLayout();
        }
    }

    @Override
    public JComponent getComponent() {

        if (dnData == null) return nullDataDisplay;

        return viewer_map.get(dnData.getName()).getViewer();
    }

    /**
     * {@code getComponent} is always called after {@code panelActivated}. The following makes sure
     * viewer has been set by then.
     */
    @Override
    public void panelActivated(Lookup lookup) {

        result = Utilities.actionsGlobalContext().lookupResult(DataNode.class);

        result.addLookupListener(this);

        dnData = getSelectedData();

        setViewer(dnData);
    }

    @Override
    public void panelDeactivated() {

        result.removeLookupListener(this);

        result = null;
    }

    @Override
    public void resultChanged(LookupEvent ev) {

        final DNData selectedData = getSelectedData();

        if (selectedData == null) return;

        Container parent = getComponent().getParent();

        dnData = selectedData;

        setViewer(dnData);

        if (parent != null) {

            parent.removeAll();
            parent.add(getComponent());
            parent.revalidate();
            parent.repaint();
        }
    }

    private DNData getSelectedData() {

        Collection<? extends DataNode> data_items = result.allInstances();

        if (!data_items.isEmpty()) {
            return data_items.iterator().next().getData();
        }

        return null;
    }

    private void setViewer(DNData dnData) {
        if (!viewer_map.containsKey(dnData.getName())) create_viewer(dnData);
    }

    private void create_viewer(DNData dnData) {


        // get drawer
        K69Data data = (K69Data) dnData.getBuilder().build();
        GeneTreeDrawer_Base gtDrawer;

        if (dnData instanceof K69DataDNDataFileBased) {

            String layoutPath =
                Builders_Coa.getGeneTree_PLayoutPath(dnData.getBuilder().getProperties()).build();

            gtDrawer = new GeneTreeDrawer_Base(data, layoutPath);

        } else {
            gtDrawer = new GeneTreeDrawer_Base(data);
        }


        // viewer: zoom & scaled
        VisualizationViewer<Node, Edge> vv      = gtDrawer.getViewer();
        final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();

        graphMouse.setMode(mode);
        vv.setGraphMouse(graphMouse);


        // mode label
        final JLabel helpLabel = new JLabel();
        JPopupMenu popup       = new JPopupMenu();

        helpLabel.setOpaque(true); //J-
        helpLabel.setText("" +
                "<html>" +
                "<b>Key-Mouse Usage:</b>" +
                "<ol>" +
                "<li>Use <b>SPACE</b> key to toggle between <u>drag/edit</u> modes. <br> " +
                    "Note the <i>changed mouse cursors</i>. In the edit mode, <br>" +
                    "drag to select multiple nodes in the bounding rectangle." +
                "</li>" +
                "<li>Use mouse <b>scroll</b> to <u>zoom</u> in/out</li>"+
                "</ol>"+
                "</html>");//J+

        popup.add(helpLabel);
        vv.addMouseListener(new GUIUtil.PopupListener(popup));


        // key-binding
        vv.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "toggleMode");

        ActionMap actionMap = vv.getActionMap();

        actionMap.put("toggleMode", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {

                switch (mode) {

                    case TRANSFORMING :
                        mode = ModalGraphMouse.Mode.PICKING;

                        break;

                    case PICKING :
                        mode = ModalGraphMouse.Mode.TRANSFORMING;
                }

                graphMouse.setMode(mode);
            }

        });
        vv.scaleToLayout(new CrossoverScalingControl());


        // register & attach listener
        viewer_map.put(dnData.getName(), gtDrawer);
        dnData.addDNDataChangeListener(new DNDataChangeListener() {

            @Override
            public void onDataChanged(DNDataChangeEvent event) {
                viewer_map.remove(event.getData().getName());
            }

        });
    }

    @Override
    public Lookup getLookup() {
        return null;
    }
}
